﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace Wyse.Server.Logging
{
    /// <summary>
    /// Enumeration of log levels
    /// </summary>
    public enum LogLevel
    {
        Fatal,
        Error,
        Warning,
        Info,
        Trace
    }

    /// <summary>
    /// Static class providing logging support
    /// </summary>
    public static class Log
    {
        /// <summary>
        /// Outputs a Trace message
        /// </summary>
        public static void Trace(String message)
        {
            Print(LogLevel.Trace, message);
        }

        /// <summary>
        /// Outputs a Trace message
        /// </summary>
        public static void Trace(String message, params Object[] args)
        {
            Print(LogLevel.Trace, message, args);
        }

        /// <summary>
        /// Outputs an Info message
        /// </summary>
        public static void Info(String message)
        {
            Print(LogLevel.Info, message);
        }

        /// <summary>
        /// Outputs an Info message
        /// </summary>
        public static void Info(String message, params Object[] args)
        {
            Print(LogLevel.Info, message, args);
        }

        /// <summary>
        /// Outputs an Warning message
        /// </summary>
        public static void Warning(String message)
        {
            Print(LogLevel.Warning, message);
        }

        /// <summary>
        /// Outputs an Warning message
        /// </summary>
        public static void Warning(String message, params Object[] args)
        {
            Print(LogLevel.Warning, message, args);
        }

        /// <summary>
        /// Outputs an Error message
        /// </summary>
        public static void Error(String message)
        {
            Print(LogLevel.Error, message);
        }

        /// <summary>
        /// Outputs an Error message
        /// </summary>
        public static void Error(String message, params Object[] args)
        {
            Print(LogLevel.Error, message, args);
        }

        /// <summary>
        /// Outputs a message to the log
        /// </summary>
        public static void Print(LogLevel level, String message)
        {
            OutputMessage(level, message);
        }

        /// <summary>
        /// Outputs a message to the log
        /// </summary>
        public static void Print(LogLevel level, String message, params Object[] args)
        {
            OutputMessage(level, String.Format(message, args));
        }

        /// <summary>
        /// Prints the details of the specified exception
        /// </summary>
        /// <param name="level"></param>
        /// <param name="ex"></param>
        public static void PrintFullException(LogLevel level, String message, Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(message);
            sb.AppendLine(ex.ToString());

            OutputMessage(level, sb.ToString());
        }

        /// <summary>
        /// Adds a listener
        /// </summary>
        public static void AddListener(ILogListener listener)
        {
            lock (sListeners)
            {
                sListeners.Add(listener);
            }
        }

        /// <summary>
        /// Disposes all listeners which implement IDisposable
        /// </summary>
        public static void CloseAllListeners()
        {
            lock (sListeners)
            {
                foreach (var l in sListeners)
                {
                    IDisposable disp = l as IDisposable;
                    if (disp != null)
                        disp.Dispose();
                }

                sListeners.Clear();
            }
        }

        private static void OutputMessage(LogLevel level, String message)
        {
            lock (sListeners)
            {
                sSb.Length = 0;

                String threadName = System.Threading.Thread.CurrentThread.Name;
                if (String.IsNullOrEmpty(threadName))
                    threadName = "worker";

                // format the message
                sSb
                    .Append(level)
                    .Append(' ')
                    .Append(DateTime.Now)
                    .Append(' ')
                    .Append('[').Append(threadName).Append(']')
                    .Append(':').Append(' ')
                    .Append(message);

                String msg = sSb.ToString();

                int cnt = sListeners.Count;
                for (int i = 0; i < cnt; ++i)
                    sListeners[i].LogMessage(msg);
            }
        }

        private static StringBuilder sSb = new StringBuilder();
        private static List<ILogListener> sListeners = new List<ILogListener>();
    }
}
